#ifndef BOX_ASIO_TRANS_IMPL_H_INCLUDED
#define BOX_ASIO_TRANS_IMPL_H_INCLUDED

#include <Core/Box/ITransportLayer.h>
#include <Core/Box/IBoxCommandIFace.h>
#include <Core/SyncedCommandIOStream.h>

#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>

namespace BoxCore {

namespace Aux {
/**
 * The AsioTransportContext 
 */
class AsioTransportContext : public Core::SyncedCommandIOStream {
public :
	explicit AsioTransportContext(boost::asio::ip::tcp::socket & socket) :
		Core::SyncedCommandIOStream(),
		m_socket(socket) 
	{ 
	}

	~AsioTransportContext() { }
	
	virtual std::size_t write(std::vector<boost::uint8_t> const & inBytes) 
		{ return boost::asio::write(m_socket, boost::asio::buffer(inBytes, inBytes.size())); }

	virtual std::size_t read(std::vector<boost::uint8_t> & outBytes) 
		{ return boost::asio::read(m_socket, boost::asio::buffer(outBytes, outBytes.size())); }

	virtual std::size_t readAtLeast(
		std::vector<boost::uint8_t> & outBytes, std::size_t bytesPos, std::size_t atLeast) 
	{ 
		// TODO impl this
		return 0; 
	} 

	virtual void close() 
		{ m_socket.close(); }
	
private :
	boost::asio::ip::tcp::socket & m_socket;

};

typedef boost::shared_ptr<AsioTransportContext> AsioTransportContextPtr;

typedef boost::asio::ip::tcp::socket SocketType;

/**
 * The BoxAsioTransportSession 
 * TODO Add life time managment!
 */
class BoxAsioTransportSession : 
	private boost::noncopyable , 
	public boost::enable_shared_from_this<BoxAsioTransportSession> 
{
public :
	BoxAsioTransportSession(IBoxCommandIFacePtr commandIFace, boost::asio::io_service & serviceRef) 
		: m_commandIFace(commandIFace), m_socket(serviceRef) { }
	virtual ~BoxAsioTransportSession() { }
	
	inline SocketType & getSocket() 
		{ return m_socket; }

	inline void handleRequest() 
	{
		AsioTransportContext * asioContext = new AsioTransportContext(m_socket);
		Core::SyncedCommandIOStreamPtr stream(asioContext);
		if (m_commandIFace->spawn(stream,
			boost::bind(&BoxAsioTransportSession::commandIFaceSpawnCompeletion, 
				shared_from_this(), _1, _2)))
		{
			// TODO Log this ?
			return;
		}
	}
	
	/**
	 * 
	 */
	inline void interrupt() 
	{ 
		// TODO impl this! 
	}

private :
	void commandIFaceSpawnCompeletion(int error, Core::SyncedCommandIOStreamPtr stream) 
	{
		if (error != 0) {
			// TODO log this?
		}
		stream->close(); // same as m_socket.close();
	}

	SocketType m_socket;
	IBoxCommandIFacePtr m_commandIFace;

};

typedef boost::shared_ptr<BoxAsioTransportSession> BoxAsioTransportSessionPtr;

inline void handleRequestSharedPtrAdapter(BoxAsioTransportSessionPtr session) 
	{ if (session) session->handleRequest(); }

} // namespace Aux

/**
 *
 */
struct BoxAsioTransportParams {
	std::string host;	// host 
	int port;			// port
	int maxSessions;	// max session per this transport implementation
};

/**
 *
 */
class BoxAsioTransport : public ITransportLayer {
public :
	explicit BoxAsioTransport(BoxAsioTransportParams const & params);
	virtual ~BoxAsioTransport() throw ();
	
	virtual void performAsync(IBoxCommandIFacePtr commandIFace) throw (TransportLayerException); 
	virtual void interrupt(bool closeGraceful) throw (TransportLayerException);
	virtual void wait() throw ();

	void waitNextAccept(
		Aux::BoxAsioTransportSessionPtr prevSession, boost::system::error_code const & error);

private :
	void asioIOServiceLoop();

	inline void startNewSessionAsync(Aux::BoxAsioTransportSessionPtr newSession)
	{
		boost::lock_guard<boost::mutex> guard(m_sessions.lock);
		m_sessions.list.push_back(newSession);
		m_sockIOService.post(boost::bind(Aux::handleRequestSharedPtrAdapter, newSession));
	}

	BoxAsioTransportParams const m_params;
	boost::asio::io_service m_sockIOService;
	boost::asio::ip::tcp::acceptor m_sockAcceptor;
	boost::scoped_ptr<boost::thread> m_sockIOServiceLoop;
	IBoxCommandIFacePtr m_commandIface;
	
	struct { 
		std::list<Aux::BoxAsioTransportSessionPtr> list;
		boost::mutex lock;
	} m_sessions;
};

}

#endif

